package com.hzjh.base.web.util

import android.content.Context
import android.os.Looper
import android.webkit.MimeTypeMap
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import com.hzjh.base.web.cache.IntelligentCache
import com.hzjh.base.web.core.JHWebView
import com.hzjh.base.web.okhttp.CallBackUtil
import com.hzjh.base.web.okhttp.OkhttpUtil
import com.hzjh.base.web.process.BridgeCommand
import com.hzjh.base.web.process.BridgeUtil
import com.hzjh.base.web.process.CommonBridge
import com.hzjh.base.web.process.PreLoadUrlBridgeI
import kotlinx.coroutines.runBlocking
import okhttp3.Call
import okio.ByteString.Companion.encodeUtf8
import java.io.File

object WebCacheUtil : PreLoadUrlBridgeI{
    private const val TAG="WebCacheUtil"
    private var webViewCache= IntelligentCache<JHWebView>(2) //已经缓存链接
    private var urlCacheIngList=ArrayList<String>() //待缓存链接
    private var ifQueueIdle:Boolean=false //true会一直执行 false只执行一遍

    fun init() {
        //跨进程web预加载使用
        CommonBridge.preLoadUrlBridgeI=this
    }
    //跨进程预加载H5
    fun preLoadUrl(cacheUrl: String){
        BridgeUtil.sendCommonData(BridgeCommand.Common.preLoadUrl, cacheUrl,true)
    }

    //缓存单个url
    override fun preLoadUrl(context: Context, cacheUrl: String, addJsBridge:((jhWebView: JHWebView) -> Unit)?) {
        var urlList=ArrayList<String>()
        urlList.add(cacheUrl)
        preLoadUrl(context,urlList,addJsBridge)
    }
    //批量缓存单个url
    fun preLoadUrl(context: Context, urlList: List<String>, addJsBridge:((jhWebView: JHWebView) -> Unit)?){
        for (cacheUrl in urlList) {
            if(webViewCache.containsKey(cacheUrl)){
                //已缓存
                WebLog.d(TAG,"已缓存 $cacheUrl")
                continue
            }
            if(urlCacheIngList.indexOf(cacheUrl)!=-1){
                //已在待缓存列表
                WebLog.d(TAG,"已在待缓存列表 $cacheUrl")
                continue
            }
            //加入待缓存列表
            urlCacheIngList.add(cacheUrl)
        }

        startPreLoadUrl(context,addJsBridge)
    }
    //清理已缓存webview
    fun clearPreWebView(){
        val it: MutableIterator<Map.Entry<String, JHWebView>> = webViewCache.cacheMap.entries.iterator()
        while (it.hasNext()) {
            val item = it.next()
            WebLog.d(TAG,"clearPreWebView ${item.key}")
            item.value.onDestroy()
            it.remove()
        }
    }
    //获取已缓存webview
    fun getPreWebView(loadUrl:String): JHWebView?{
        var cacheWebView=webViewCache.get(loadUrl)
        cacheWebView?.let {
            webViewCache.remove(loadUrl)
        }
        return cacheWebView
    }

    //开始预加载url
    private fun startPreLoadUrl(context: Context, addJsBridge:((jhWebView: JHWebView) -> Unit)?){
        if(ifQueueIdle){
            return
        }
        Looper.myQueue().addIdleHandler {
            if(urlCacheIngList.size>0){
                var cacheUrl= urlCacheIngList[0]
                WebLog.d(TAG,"缓存ing地址 $cacheUrl")
                var webView=JHWebViewPool.getInstance().getPreWebView(context)
                addJsBridge?.invoke(webView)
                webView.guangLoadUrl(url=cacheUrl)
                urlCacheIngList.remove(cacheUrl)
                webViewCache.put(cacheUrl,webView)
            }
            ifQueueIdle= urlCacheIngList.size>0
            ifQueueIdle
        }
    }

    //资源拦截加载
    fun shouldInterceptRequest(view: WebView,
                               request: WebResourceRequest): WebResourceResponse? {
        WebLog.d(TAG, "拦截资源HEADER：${request.requestHeaders}")

        var webResourceResponse: WebResourceResponse? = null
        // 如果是 assets 目录下的文件 暂时没有本地资源
//            if (isAssetsResource(request)) {
//                webResourceResponse = assetsResourceRequest(view.context, request)
//            }

        // 如果是可以缓存的文件
        if (isCacheResource(request)) {
            webResourceResponse = cacheResourceRequest(view.context, request)
        }
        if(webResourceResponse!=null){
            WebLog.d(TAG,"SUCCESS--命中缓存 ${request.url}")
        }else{
            WebLog.d(TAG,"FAIL--没有命中缓存 ${request.url}")
        }
        return webResourceResponse
    }

    //是否本地资源文件
    private fun isAssetsResource(webRequest: WebResourceRequest?): Boolean {
        val url = webRequest?.url.toString()
        return url.startsWith("file:///android_asset/")
    }

    /**
     * assets 文件请求
     */
    private fun assetsResourceRequest(
        context: Context,
        webRequest: WebResourceRequest
    ): WebResourceResponse? {
        val url = webRequest.url.toString()
        try {
            val filenameIndex = url.lastIndexOf("/") + 1
            val filename = url.substring(filenameIndex)
            val suffixIndex = url.lastIndexOf(".")
            val suffix = url.substring(suffixIndex + 1)
            val webResourceResponse = WebResourceResponse(
                getMimeTypeFromUrl(url),
                "UTF-8",
                context.assets.open("$suffix/$filename")
            )
            webResourceResponse.responseHeaders = mapOf("access-control-allow-origin" to "*")
            return webResourceResponse
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return null
    }

    /**
     * 根据 url 获取文件类型
     */
    private fun getMimeTypeFromUrl(url: String): String {
        try {
            val extension = MimeTypeMap.getFileExtensionFromUrl(url)
            if (extension.isNotBlank() && extension != "null") {
                if (extension == "json") {
                    return "application/json"
                }
                return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension) ?: "*/*"
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return "*/*"
    }

    /**
     * 判断是否是可以被缓存等资源
     */
    private fun isCacheResource(webRequest: WebResourceRequest): Boolean {
        //根据资源后缀类型缓存 缓存html文件会影响是否真人判断检测 人机检测页面会空白
        val url = webRequest.url.toString()
        val extension = MimeTypeMap.getFileExtensionFromUrl(url)
        WebLog.d(TAG,"资源类型--$extension---$url")
        return extension == "ico" || extension == "bmp" || extension == "gif"
                || extension == "jpeg" || extension == "jpg" || extension == "png"
                || extension == "svg" || extension == "webp" || extension == "css"
                || extension == "js" || extension == "json" || extension == "eot"
                || extension == "otf" || extension == "ttf" || extension == "woff"
                || extension == "woff2"
    }

    /**
     * 可缓存文件请求
     */
    private fun cacheResourceRequest(
        context: Context,
        webRequest: WebResourceRequest
    ): WebResourceResponse? {
        val url = webRequest.url.toString()
        val mimeType = getMimeTypeFromUrl(url)

        // WebView 中的图片利用 Glide 加载(能够和App其他页面共用缓存) 发现走glide比直接取本地慢
//        if (isImageResource(webRequest)) {
//            return try {
//                val file = Glide.with(context).download(url).submit().get()
//                val webResourceResponse = WebResourceResponse(mimeType, "UTF-8", file.inputStream())
//                webResourceResponse.responseHeaders = mapOf("access-control-allow-origin" to "*")
//                webResourceResponse
//            } catch (e: Exception) {
//                e.printStackTrace()
//                null
//            }
//        }

        /**
         * 其他文件缓存逻辑
         * 1.寻找缓存文件，本地有缓存直接返回缓存文件
         * 2.无缓存，下载到本地后返回
         * 注意！！！
         * 一定要确保文件下载完整，我这里采用下载完成后给文件加 "success-" 前缀的方法
         */
        val webCachePath = getWebViewCachePath(context)
        val destFileName=url.encodeUtf8().md5().hex()
        val cacheFilePath =
            webCachePath + File.separator + "success-" + destFileName // 自定义文件命名规则
        val cacheFile = File(cacheFilePath)
        if (!cacheFile.exists() || !cacheFile.isFile) { // 本地不存在 则开始下载
            // 下载文件

            val sourceFilePath = webCachePath + File.separator + destFileName
            val sourceFile = File(sourceFilePath)

            //资源文件下载
            runBlocking {
                try {
                    OkhttpUtil.okHttpDownloadFile(url,null,webRequest.requestHeaders,object :
                        CallBackUtil.CallBackFile(webCachePath,destFileName) {
                        override fun onFailure(call: Call?, e: java.lang.Exception?) {
                            WebLog.d(TAG,"FAIL--Download缓存失败 $url")
                        }

                        override fun onResponse(call: Call?, response: File?) {
                            WebLog.d(TAG,"SUCCESS--Download缓存成功 $url")
                            // 下载完成后增加 "success-" 前缀 代表文件无损 【防止io流被异常中断导致文件损坏 无法判断】
                            response?.renameTo(cacheFile)
                        }
                    })
                } catch (e: Exception) {
                    e.printStackTrace()
                    // 发生异常删除文件
                    sourceFile.deleteOnExit()
                    cacheFile.deleteOnExit()
                }
            }
        }

        // 缓存文件存在则返回
        if (cacheFile.exists() && cacheFile.isFile) {
            val webResourceResponse =
                WebResourceResponse(mimeType, "UTF-8", cacheFile.inputStream())
            webResourceResponse.responseHeaders = mapOf("access-control-allow-origin" to "*")
            return webResourceResponse
        }
        return null
    }

    /**
     * 判断是否是图片
     * 有些文件存储没有后缀，也可以根据自家服务器域名等等
     */
    private fun isImageResource(webRequest: WebResourceRequest): Boolean {
        val url = webRequest.url.toString()
        val extension = MimeTypeMap.getFileExtensionFromUrl(url)
        return extension == "ico" || extension == "bmp" || extension == "gif"
                || extension == "jpeg" || extension == "jpg" || extension == "png"
                || extension == "svg" || extension == "webp"
    }

    /**
     * 获取 WebView 缓存文件目录
     */
    fun getWebViewCachePath(context: Context): String{
        return context.filesDir.absolutePath + "/webCache"
    }

    /**
     * 删除本地H5缓存文件夹
     */
    fun clearCacheFolder(cacheFolder: File){
        try {
            if (cacheFolder.isDirectory) {
                cacheFolder.listFiles()?.let {
                    for (child in it) {
                        if (child.isDirectory) {
                            clearCacheFolder(child)
                        }
                        child.delete()
                    }
                }
            }
        }catch (e:Exception){
            e.printStackTrace()
        }
    }
}